package com.isoftstone.util;

import com.isoftstone.project.buttjoint.domain.MonitorTest;
import com.isoftstone.project.buttjoint.mapper.MonitorTestMapper;
import com.isoftstone.project.facilities.management.domain.FacilitiesManagement;
import com.isoftstone.project.facilities.management.mapper.FacilitiesManagementMapper;
import com.isoftstone.project.system.dict.domain.Dict;
import com.isoftstone.project.system.dict.mapper.DictMapper;
import gnu.io.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * 串口参数的配置 串口一般有如下参数可以在该串口打开以前进行配置： 包括串口号，波特率，输入/输出流控制，数据位数，停止位和奇偶校验。
 */
// 注：串口操作类一定要继承SerialPortEventListener
    @Slf4j
    @Service
public class SerialPortUtils implements SerialPortEventListener {
    // 检测系统中可用的通讯端口类
    private CommPortIdentifier commPortId;
    // 枚举类型
    private Enumeration<CommPortIdentifier> portList;
    // RS232串口
    private SerialPort serialPort;
    // 输入流
    private InputStream inputStream;
    // 输出流
    private OutputStream outputStream;
    // 保存串口返回信息
    private StringBuffer data = new StringBuffer();
    // 保存串口返回信息十六进制
    private String dataHex;
    private boolean guanbi = false;
@Autowired
FacilitiesManagementMapper facilitiesManagementMapper;
@Autowired
    DictMapper dictMapper;
@Autowired
    MonitorTestMapper monitorTestMapper;
    public boolean isGuanbi() {
        return guanbi;
    }

    public void setGuanbi(boolean guanbi) {
        this.guanbi = guanbi;
    }

    /**
     * 初始化串口
     * @author LinWenLi
     * @date 2018年7月21日下午3:44:16
     * @Description: TODO
     * @param: paramConfig  存放串口连接必要参数的对象（会在下方给出类代码）
     * @return: void
     * @throws
     */
    @SuppressWarnings("unchecked")
    public void init(ParamConfig paramConfig) {
        // 获取系统中所有的通讯端口
        portList = CommPortIdentifier.getPortIdentifiers();
        // 记录是否含有指定串口
        boolean isExsist = false;
        // 循环通讯端口
        while (portList.hasMoreElements()) {
            commPortId = portList.nextElement();
            // 判断是否是串口
            if (commPortId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                // 比较串口名称是否是指定串口
                if (paramConfig.getSerialNumber().equals(commPortId.getName())) {
                    // 串口存在
                    isExsist = true;
                    // 打开串口
                    try {
                        // open:（应用程序名【随意命名】，阻塞时等待的毫秒数）
                        serialPort = (SerialPort) commPortId.open(Object.class.getSimpleName(), 2000);
                        // 设置串口监听
                        serialPort.addEventListener(this);
                        serialPort.notifyOnDataAvailable(true);//串口有数据监听
                        serialPort.notifyOnBreakInterrupt(true);//中断事件监听
                        // 设置串口通讯参数:波特率，数据位，停止位,校验方式
                        serialPort.setSerialPortParams(paramConfig.getBaudRate(), paramConfig.getDataBit(),
                                paramConfig.getStopBit(), paramConfig.getCheckoutBit());
                    } catch (PortInUseException e) {
                        throw new RuntimeException("端口被占用");
                    } catch (TooManyListenersException e) {
                        throw new RuntimeException("监听器过多");
                    } catch (UnsupportedCommOperationException e) {
                        throw new RuntimeException("不支持的COMM端口操作异常");
                    }
                    // 结束循环
                    break;
                }
            }
        }
        // 若不存在该串口则抛出异常
        if (!isExsist) {
            throw new RuntimeException("不存在该串口！");
        }
    }

    /**
     * 实现接口SerialPortEventListener中的方法 读取从串口中接收的数据
     */
    @Override
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
            case SerialPortEvent.BI: // 通讯中断
            case SerialPortEvent.OE: // 溢位错误
            case SerialPortEvent.FE: // 帧错误
            case SerialPortEvent.PE: // 奇偶校验错误
            case SerialPortEvent.CD: // 载波检测
            case SerialPortEvent.CTS: // 清除发送
            case SerialPortEvent.DSR: // 数据设备准备好
            case SerialPortEvent.RI: // 响铃侦测
            case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 输出缓冲区已清空
                break;
            case SerialPortEvent.DATA_AVAILABLE: // 有数据到达
                // 调用读取数据的方法
                try {

                    inputStream = serialPort.getInputStream();
                    // 通过输入流对象的available方法获取数组字节长度
                    byte[] bytes = null;
                    // 从线路上读取数据流
                    // 获取buffer里的数据长度
                    int bufflenth = inputStream.available();
                    System.out.println(bufflenth);

                    while (bufflenth != 0) {
                        // 初始化byte数组为buffer中数据的长度
                        bytes = new byte[bufflenth];
                        inputStream.read(bytes);
                        bufflenth = inputStream.available();
                    }
                    dataHex = bytesToHexString(bytes);
                    data.append(dataHex);
                    System.out.println("dataHex:" + dataHex);// 读取后置空流对象
                } catch (IOException e) {
                    throw new RuntimeException("读取串口数据时发生IO异常");
                }
                break;
            default:
                System.out.println(1);
                break;
        }
        String crcm = getCRCModBus(hexToByteArray(data.substring(0,data.length()-4)));
        String subcrc = data.substring(data.length()-4,data.length());
        log.info("读取数据为："+crcm);
        log.info("CRC为："+subcrc);
        log.info("数据为："+data);
        if(subcrc.equals(crcm)){
            log.info("读取数据为："+crcm);
            log.info("CRC为："+subcrc);
            log.info("数据为："+data);
           // closeSerialPort();
            setGuanbi(true);
            String sb=data.substring(4,6);//取字节数
            int sum=Integer.parseInt(sb)/4;
            int totallen = 6;
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
            String workdate=df.format(new Date());
            log.info("监测时间:"+workdate);
            String id=data.substring(0,2);//分站号
            String code=data.substring(2,4);//功能码
            String uuid=UUID.randomUUID().toString().replace("-","");//UUID
            FacilitiesManagement facilitiesManagement=  facilitiesManagementMapper.selectcode(id,code);//查询参数
            String name=facilitiesManagement.getManagementName();//名字
            String[] strArr = facilitiesManagement.getType().split(",");
            for (int i=0;i<sum;i++){
                MonitorTest monitorTest=new MonitorTest();
                String getSignInfo = strArr[i].substring(strArr[i].indexOf("(") + 1,strArr[i].indexOf(")"));//单位
                String getname = strArr[i].substring(0,strArr[i].indexOf("("));//描述
                int vs=totallen+8;
                String ww=data.substring(totallen,vs);
                totallen=vs;
                log.info("UUID："+uuid);
                log.info("分站号："+id);
                log.info("功能码："+code);
                log.info("name："+name);
                log.info("V"+(i+1)+":"+hexStringToString(ww));
                log.info("单位："+getSignInfo);
                log.info("描述："+getname);
                //安装位置
                monitorTest.setUuid(uuid);
                monitorTest.setDeviceid(id);
                monitorTest.setDevicename(name);
                monitorTest.setV(hexStringToString(ww));
                monitorTest.setVname(getname);
                monitorTest.setVcode(getSignInfo);
                monitorTest.setWorkdate(workdate);
                monitorTestMapper.insert(monitorTest);
            }
            //入库
        }
    }
    public static String getCRC2(byte[] bytes) {
        int CRC = 0x0000ffff;
        int POLYNOMIAL = 0x0000a001;

        int i, j;
        for (i = 0; i < bytes.length; i++) {
            CRC ^= (int) bytes[i];
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) == 1) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        // 交换高低位，低位在前高位在后
        CRC = ((CRC & 0x0000FF00) >> 8) | ((CRC & 0x000000FF) << 8);
        String result = Integer.toHexString(CRC);
        return result.substring(0, 2) + " " + result.substring(2, 4);
    }

    /**
     * 计算ModBus CRC16校验码
     *
     * @param bytes
     * @return
     */
    public static String getCRCModBus(byte[] bytes) {
        int CRC = 0x0000ffff;
        int POLYNOMIAL = 0x0000a001;

        int i, j;
        for (i = 0; i < bytes.length; i++) {
            CRC ^= ((int) bytes[i] & 0x000000ff);
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        CRC = ( (CRC & 0x0000FF00) >> 8) | ( (CRC & 0x000000FF ) << 8);
        return Integer.toHexString(CRC).toUpperCase();
    }
    /**
     * 读取串口返回信息
     * @author LinWenLi
     * @date 2018年7月21日下午3:43:04
     * @return: void
     */
    public void readComm() {
        try {

            inputStream = serialPort.getInputStream();
            // 通过输入流对象的available方法获取数组字节长度
            byte[] bytes = null;
            // 从线路上读取数据流
            // 获取buffer里的数据长度
            int bufflenth = inputStream.available();
            System.out.println(bufflenth);

            while (bufflenth != 0) {
                // 初始化byte数组为buffer中数据的长度
                bytes = new byte[bufflenth];
                inputStream.read(bytes);
                bufflenth = inputStream.available();
            }
            dataHex = bytesToHexString(bytes);
            System.out.println("dataHex:" + dataHex);// 读取后置空流对象

//            while ((len = inputStream.read(readBuffer)) != -1) {
//                // 直接获取到的数据
//                data = new String(readBuffer, 0, len).trim();
//                // 转为十六进制数据
//                dataHex = bytesToHexString(readBuffer);
//                System.out.println("data:" + data);
//                System.out.println("dataHex:" + dataHex);// 读取后置空流对象
//                inputStream.close();
//                inputStream = null;
//                break;
//            }

          //  closeSerialPort();
        } catch (IOException e) {
            throw new RuntimeException("读取串口数据时发生IO异常");
        }
    }
    /**
     * 合并数组
     *
     * @param firstArray  第一个数组
     * @param secondArray 第二个数组
     * @return 合并后的数组
     */
    public static byte[] concat(byte[] firstArray, byte[] secondArray) {
        if (firstArray == null || secondArray == null) {
            if (firstArray != null)
                return firstArray;
            if (secondArray != null)
                return secondArray;
            return null;
        }
        byte[] bytes = new byte[firstArray.length + secondArray.length];
        System.arraycopy(firstArray, 0, bytes, 0, firstArray.length);
        System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length);
        return bytes;
    }
    /**
     * 发送信息到串口
     * @author LinWenLi
     * @date 2018年7月21日下午3:45:22
     * @param: data
     * @return: void
     * @throws
     */
    public void sendComm(String data) {
        byte[] writerBuffer = null;
        try {
            writerBuffer = hexToByteArray(data);
        } catch (NumberFormatException e) {
            throw new RuntimeException("命令格式错误！");
        }
        try {
            outputStream = serialPort.getOutputStream();
            outputStream.write(writerBuffer);
            outputStream.flush();
        } catch (NullPointerException e) {
            throw new RuntimeException("找不到串口。");
        } catch (IOException e) {
            throw new RuntimeException("发送信息到串口时发生IO异常");
        }
    }

    /**
     * 关闭串口
     * @author LinWenLi
     * @date 2018年7月21日下午3:45:43
     * @Description: 关闭串口
     * @param:
     * @return: void
     * @throws
     */
    public void closeSerialPort() {
        try {
        if (serialPort != null) {
           //serialPort.close();
            serialPort.notifyOnDataAvailable(false);
            serialPort.removeEventListener();

            serialPort.close();
            serialPort = null;
        }
        } catch (Exception e){
                e.printStackTrace();
        }
    }

    /**
     * 十六进制串口返回值获取
     */
    public String getDataHex() {
        String result = dataHex;
        // 置空执行结果
        dataHex = null;
        // 返回执行结果
        return result;
    }

    /**
     * Hex字符串转byte
     * @param inHex 待转换的Hex字符串
     * @return 转换后的byte
     */
    public static byte hexToByte(String inHex) {
        return (byte) Integer.parseInt(inHex, 16);
    }

    /**
     * hex字符串转byte数组
     * @param inHex 待转换的Hex字符串
     * @return 转换后的byte数组结果
     */
    public static byte[] hexToByteArray(String inHex) {
        int hexlen = inHex.length();
        byte[] result;
        if (hexlen % 2 == 1) {
            // 奇数
            hexlen++;
            result = new byte[(hexlen / 2)];
            inHex = "0" + inHex;
        } else {
            // 偶数
            result = new byte[(hexlen / 2)];
        }
        int j = 0;
        for (int i = 0; i < hexlen; i += 2) {
            result[j] = hexToByte(inHex.substring(i, i + 2));
            j++;
        }
        return result;
    }

    /**
     * 数组转换成十六进制字符串
     * @param bArray []
     * @return HexString
     */
    public static final String bytesToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String ss="010308BE40E6121111111115A2";
        String sb=ss.substring(2,4);
        System.out.println(sb);
//        int sum=Integer.parseInt(sb)/4;
//        int totallen = 6;
//        for (int i=0;i<sum;i++){
//            int vs=totallen+8;
//            String ww=ss.substring(totallen,vs);
//            totallen=vs;
//            System.out.println("V"+(i+1)+":"+ww);
//        }
    }
    /**
     * 16进制转换成为string类型字符串
     * @param s
     * @return
     */
    public static String hexStringToString(String s) {
        if (s == null || s.equals("")) {
            return null;
        }
        s = s.replace(" ", "");
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, "UTF-8");
            new String();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return s;
    }
}